﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.IO;
using LetiLib.Connection;
using System.Drawing;
using System.Diagnostics;

namespace LetiLib.Data
{
    public class DataPackage
    {
        public const ushort HEADER_LENGTH = sizeof(byte) + // sizeof ((byte)TAG.SERVER_TAG)
                                            sizeof(byte) + // sizeof ((byte)TAG.DATA_PACKAGE_HEADER)
                                            sizeof(ushort) + // size of id
                                            sizeof(ushort)   // size of length
                                            ;
        ushort ID;
        byte[] data;
        public DataPackage(ushort id, byte[] buffer)
        {
            ID = id;
            data = new byte[buffer.Length + HEADER_LENGTH ];
            data[0] = (byte)COMMAND.SERVER_TAG;
            data[1] = (byte)COMMAND.DATA_PACKAGE_HEADER;
            BitConverter.GetBytes(id).CopyTo(data,2);
            BitConverter.GetBytes(((ushort)buffer.Length)).CopyTo(data, 4);
            buffer.CopyTo(data, 6);
        }

        public byte[] Data
        {
            get
            {
                return data;
            }
        }
    }

    public class DataInfo
    {
        public ushort Index { get; set; }
        public ushort Width { get; set; }
        public ushort Height { get; set; }
        public ushort BPP { get; set; }// Bits per pixels
        //string version;
        //public string Version{get{return version;}}
        public DataInfo(ushort width, ushort height, ushort bpp)
        {
            Index = 1;
            Width = width;
            Height = height;
            BPP = bpp;
        }

        public DataInfo(ushort index,ushort width, ushort height, ushort bpp)
        {
            Index = index;
            Width = width;
            Height = height;
            BPP = bpp;
        }

        public byte[] Data
        {
            get
            {
                byte[] buffer = new byte[10];
                buffer[0] = (byte)COMMAND.SERVER_TAG;
                buffer[1] = (byte)COMMAND.DATA_INFO_HEADER;
                BitConverter.GetBytes(Index).CopyTo(buffer, 2);
                BitConverter.GetBytes(Width).CopyTo(buffer, 4);
                BitConverter.GetBytes(Height).CopyTo(buffer, 6);
                BitConverter.GetBytes(BPP).CopyTo(buffer, 8);
                return buffer;
            }
        }
    }

    public class PlaylistInfo
    {
        public uint Delay { get; set; }
        public ushort PackageCount { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }// Bits per pixels
        //string version;
        //public string Version{get{return version;}}
        public PlaylistInfo()
        {
            Delay = 0;
            PackageCount = 0;
        }


        public PlaylistInfo(uint delay, ushort pCount)
        {
            Delay = delay;
            PackageCount = pCount;
        }

        public byte[] Data
        {
            get
            {
                byte[] buffer = new byte[8];
                buffer[0] = (byte)COMMAND.SERVER_TAG;
                buffer[1] = (byte)COMMAND.PLAYLIST_INFO_HEADER;
                BitConverter.GetBytes(PackageCount).CopyTo(buffer, 2);
                BitConverter.GetBytes(Delay).CopyTo(buffer, 4);
                return buffer;
            }
        }
    }

    public class PlaylistData
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public PlaylistInfo Info { get; set; }
        public List<ProgramData> packages { get; set; }
        
        public PlaylistData()
        {
            Info = new PlaylistInfo();
            packages = new List<ProgramData>();
        }

        public override string ToString()
        {
            return "Playlist: "
                   + "\n   - ID:" + ID
                   + "\n   - Name:" + Name
                   + "\n   - Delay:" + Info.Delay
                   + "\n   - StartDate:" + Info.StartDate
                   + "\n   - EndDate:" + Info.EndDate
                   + "\n   - PackageCount:" + Info.PackageCount;
        }
    }

    public class ProgramData
    {
        DataInfo info;
        List<DataPackage> packages;
        public DataInfo Info
        {
            get
            {
                return info;
            }
            set
            {
                info = value;
            }
        }
        public List<DataPackage> Pakages
        {
            get
            {
                return packages;
            }
            set
            {
                packages = value;
            }
        }
        public ProgramData(DataInfo ainfo, byte[] data)
        {

            Info = ainfo;
            packages = new List<DataPackage>();

            int dataLength = (int)CONST.DATA_PACKAGE_LENGTH - // total tcp package
                            DataPackage.HEADER_LENGTH - // header length
                            1; // CRC byte
            
            
            byte[] temp = new byte[dataLength];
            int nPackages = 0;
            if (data.Length % dataLength == 0)
                nPackages = data.Length / dataLength;
            else
                nPackages = data.Length / dataLength + 1;
            for (int i = 0, k = 0; i < nPackages - 1; i++, k += dataLength)
            {
                Array.Copy(data, k, temp, 0, dataLength);

                packages.Add(new DataPackage((ushort)i, temp));
            }

            int lastBufferSize = data.Length - (nPackages - 1) * dataLength;
            byte[] lastBuffer = new byte[lastBufferSize];
            if (lastBufferSize != 0)
            {
                Array.Copy(data, (nPackages - 1) * dataLength, lastBuffer, 0, lastBufferSize);
                packages.Add(new DataPackage((ushort)(nPackages - 1), lastBuffer));
            }

        }

        public static ProgramData FromFile(string path)
        {
            Bitmap bitmap = null;
            try
            {
                bitmap = (Bitmap)Image.FromFile(path);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                
            }

            if (bitmap == null) return null;

            ushort BPP = 2;
            // create data info
            DataInfo info = new DataInfo(1,
                (ushort)bitmap.Width,
                (ushort)bitmap.Height, BPP);// 2 bit per pixel
            
            // alloc memory array
            BitArray bArray = new BitArray(bitmap.Width * bitmap.Height * BPP);
            // copy pixel from bitmap to led bit matrix
            for (int j = 0; j < bitmap.Width; j++)
            {
                for (int i = 0; i < bitmap.Height; i++)
                {
                    Color col = bitmap.GetPixel(j, i);
                    if (col.ToArgb() == Color.Red.ToArgb())
                    {
                        bArray[i * bitmap.Width * BPP + j] = true;
                        bArray[i * bitmap.Width * BPP + j + 1] = false;
                    }
                    else if (col.ToArgb() == Color.Lime.ToArgb())
                    {
                        bArray[i * bitmap.Width * BPP + j] = false;
                        bArray[i * bitmap.Width * BPP + j + 1] = true;
                    }
                    else if (col.ToArgb() == Color.Yellow.ToArgb())
                    {
                        bArray[i * bitmap.Width * BPP + j] = true;
                        bArray[i * bitmap.Width * BPP + j + 1] = true;
                    }
                    else
                    {
                        bArray[i * bitmap.Width * BPP + j] = false;
                        bArray[i * bitmap.Width * BPP + j + 1] = false;
                    }
                }
            }

            byte[] imageData = new byte[(bArray.Length + 7) / 8 + 5];

            // Data format:
            // <width><height><BPP><data>
            // <width> 2 bytes short
            byte[] temp = System.BitConverter.GetBytes((ushort)bitmap.Width);
            imageData[0] = temp[0];
            imageData[1] = temp[1];
            // <height> 2 bytes short
            temp = System.BitConverter.GetBytes((ushort)bitmap.Height);
            imageData[2] = temp[0];
            imageData[3] = temp[1];
            // <BPP> 1 bytes short 
            // bpp can set to 1,2 or 24 bits per pixel
            imageData[4] = (byte)BPP;
            // <data>  bArray.Length/8 bytes
            bArray.CopyTo(imageData, 5);

            return new ProgramData(info, imageData);
        }
    }

    public class FirmwareInfo
    {
        public ushort Version { get; set; }
        public int Count { get; set; }
        //string version;
        //public string Version{get{return version;}}
        public FirmwareInfo(ushort version, int count)
        {
            Version = version;
            Count = count;
            //version = iVersion;
        }

        public byte[] Data
        {
            get
            {
                byte[] buffer = new byte[8];
                buffer[0] = (byte)COMMAND.SERVER_TAG;
                buffer[1] = (byte)COMMAND.FIRMWARE_INFO_HEADER;
                BitConverter.GetBytes(Version).CopyTo(buffer, 2);
                BitConverter.GetBytes(Count).CopyTo(buffer, 4);
                //version.ToCharArray().CopyTo(buffer,9);
                //Array.Copy(version.ToCharArray(),0, buffer, 10, version.Length);
                return buffer;
            }
        }
    }

    public class FirmwarePackage
    {
        ushort ID;
        byte[] data;
        public FirmwarePackage(ushort id, byte[] buffer)
        {
            ID = id;
            data = new byte[buffer.Length + 6];
            data[0] = (byte)COMMAND.SERVER_TAG;
            data[1] = (byte)COMMAND.FIRMWARE_PACKAGE_HEADER;
            BitConverter.GetBytes(id).CopyTo(data, 2);
            BitConverter.GetBytes((ushort)buffer.Length).CopyTo(data, 4);
            buffer.CopyTo(data, 6);
        }

        public byte[] Data
        {
            get
            {
                return data;
            }
        }
    }

    public class BlackboxFirmware
    {
        FirmwareInfo info;
        List<FirmwarePackage> packages;
        public FirmwareInfo Info
        {
            get
            {
                return info;
            }
            set
            {
                info = value;
            }
        }
        public List<FirmwarePackage> Pakages
        {
            get
            {
                return packages;
            }
            set
            {
                packages = value;
            }
        }
        public BlackboxFirmware(string path)
        {
            HexParser parser = new HexParser();
            parser.parse(path);
            //byte[] buffer = File.ReadAllBytes(path);
            //File.WriteAllBytes("C:\\out.txt", parser.data.ToArray());
            // Send Firmware Info
            info = new FirmwareInfo(101, parser.data.Length);

            packages = new List<FirmwarePackage>();

            byte[] temp = new byte[CONST.FIRMWARE_PACKAGE_LENGTH];
            int nPackages = 0;
            if (parser.data.Length % CONST.FIRMWARE_PACKAGE_LENGTH == 0)
                nPackages = parser.data.Length / CONST.FIRMWARE_PACKAGE_LENGTH;
            else
                nPackages = parser.data.Length / CONST.FIRMWARE_PACKAGE_LENGTH + 1;

            for (int i = 0, k = 0; i < nPackages - 1; i++, k += CONST.FIRMWARE_PACKAGE_LENGTH)
            {
                Array.Copy(parser.data, k, temp, 0, CONST.FIRMWARE_PACKAGE_LENGTH);
                //FirmwarePackage package = 

                packages.Add(new FirmwarePackage((ushort)i, temp));
            }

            int lastBufferSize = parser.data.Length - (nPackages - 1) * CONST.FIRMWARE_PACKAGE_LENGTH;
            byte[] lastBuffer = new byte[lastBufferSize];
            if (lastBufferSize != 0)
            {
                Array.Copy(parser.data, (nPackages - 1) * CONST.FIRMWARE_PACKAGE_LENGTH, lastBuffer, 0, lastBufferSize);
                packages.Add(new FirmwarePackage((ushort)(nPackages - 1), lastBuffer));
            }
        }
    }

    public class FontInfo
    {
        public ushort ID { get; set; }
        public int DataSize { get; set; }
        //string version;
        //public string Version{get{return version;}}
        public FontInfo(ushort version, int size)
        {
            ID = version;
            DataSize = size;
            //version = iVersion;
        }

        public byte[] Data
        {
            get
            {
                byte[] buffer = new byte[8];
                buffer[0] = (byte)COMMAND.SERVER_TAG;
                buffer[1] = (byte)COMMAND.FONT_INFO_HEADER;
                BitConverter.GetBytes(ID).CopyTo(buffer, 2);
                BitConverter.GetBytes(DataSize).CopyTo(buffer, 4);
                //version.ToCharArray().CopyTo(buffer,9);
                //Array.Copy(version.ToCharArray(),0, buffer, 10, version.Length);
                return buffer;
            }
        }
    }

    public class FontPackage
    {
        ushort ID;
        byte[] data;
        public FontPackage(ushort id, byte[] buffer)
        {
            ID = id;
            data = new byte[buffer.Length + 6];
            data[0] = (byte)COMMAND.SERVER_TAG;
            data[1] = (byte)COMMAND.FONT_PACKAGE_HEADER;
            BitConverter.GetBytes(id).CopyTo(data, 2);
            BitConverter.GetBytes((ushort)buffer.Length).CopyTo(data, 4);
            buffer.CopyTo(data, 6);
        }

        public byte[] Data
        {
            get
            {
                return data;
            }
        }
    }

    public class FontFirmware
    {
        FontInfo info;
        List<FontPackage> packages;
        public FontInfo Info
        {
            get
            {
                return info;
            }
            set
            {
                info = value;
            }
        }
        public List<FontPackage> Pakages
        {
            get
            {
                return packages;
            }
            set
            {
                packages = value;
            }
        }
        public FontFirmware(string path)
        {
            // Load font info
            BinaryReader br = new BinaryReader(new FileStream(path,FileMode.Open));
            ushort fontID = br.ReadByte();
            int length = (int)(new FileInfo(path)).Length;
            info = new FontInfo(fontID, length);

            byte[] data = new byte[length];
            data[0] = (byte)fontID;
            br.ReadBytes(length - 1).CopyTo(data,1);
            br.Close();
            packages = new List<FontPackage>();

            byte[] temp = new byte[CONST.FONT_PACKAGE_LENGTH];
            int nPackages = 0;
            if (length % CONST.FONT_PACKAGE_LENGTH == 0)
                nPackages = length / CONST.FONT_PACKAGE_LENGTH;
            else
                nPackages = length / CONST.FONT_PACKAGE_LENGTH + 1;

            for (int i = 0, k = 0; i < nPackages - 1; i++, k += CONST.FONT_PACKAGE_LENGTH)
            {
                Array.Copy(data, k, temp, 0, CONST.FONT_PACKAGE_LENGTH);
                //FirmwarePackage package = 

                packages.Add(new FontPackage((ushort)i, temp));
            }

            int lastBufferSize = data.Length - (nPackages - 1) * CONST.FONT_PACKAGE_LENGTH;
            byte[] lastBuffer = new byte[lastBufferSize];
            if (lastBufferSize != 0)
            {
                Array.Copy(data, (nPackages - 1) * CONST.FONT_PACKAGE_LENGTH, lastBuffer, 0, lastBufferSize);
                packages.Add(new FontPackage((ushort)(nPackages - 1), lastBuffer));
            }
        }
    }
    

    class HexParser
    {
        List<byte> bytearray;
        int dataOffset;

        public byte[] data
        {
            get
            {
                return bytearray.ToArray();
            }
        }
        public int Offset
        {
            get
            {
                return dataOffset;
            }
        }
        public string parse(string filepath)
        {
            string strContent = "";
            string strConverted = "";
            dataOffset = -1;
            if (File.Exists(filepath))
            {
                StreamReader hexReader = new StreamReader(filepath);

                try
                {
                    char startcode;
                    string temp = "";
                    int bytecount;
                    int address;
                    int recordtype;
                    string data;
                    int strLen = 0;
                    int unitLength = 0;
                    bytearray = new List<byte>();
                    do
                    {
                        strContent = hexReader.ReadLine();
                        strLen = strContent.Length;

                        //startcode = strContent.Substring(0,1);
                        bytecount = Convert.ToInt32(strContent.Substring(1, 2), 16);
                        address = Convert.ToInt32(strContent.Substring(3, 4), 16);
                        recordtype = Convert.ToInt32(strContent.Substring(7, 2), 16);

                        if (recordtype == 0)
                        {
                            if (dataOffset == -1)
                            {
                                dataOffset = address;
                            }
                            data = strContent.Substring(9, strLen - 11);
                            if (bytecount != 0)
                            {
                                unitLength = data.Length / bytecount;
                                for (int i = 0; i < data.Length; i += unitLength)
                                {
                                    temp = data.Substring(i, unitLength);
                                    byte value = Convert.ToByte(temp, 16);
                                    bytearray.Add(value);
                                }

                            }
                        }
                    } while (hexReader.Peek() != -1);
                    /*
                    // Convert data to big endian order
                    byte tempLowbyte = 0;
                    byte tempHighbyte = 0;
                    for (int i = 0; i < bytearray.Count; i += 4)
                    {
                        tempLowbyte = bytearray[i + 3];
                        bytearray[i + 3] = bytearray[i];
                        bytearray[i] = tempLowbyte;

                        tempHighbyte = bytearray[i + 2];
                        bytearray[i + 2] = bytearray[i + 1];
                        bytearray[i + 1] = tempHighbyte;
                        /*
                        strConverted += bytearray[i].ToString() +
                            bytearray[i + 1].ToString() +
                            bytearray[i + 2].ToString() +
                            bytearray[i + 3].ToString() ;
                        if (i %2 != 0)
                        {
                            strConverted += " ";
                        }
                        else
                        {
                            strConverted += "\r\n";
                        }
                    }

                */
                    return strConverted;
                }
                catch (Exception e)
                {
                    strConverted += e.Message;
                }
            }
            return strConverted;
        }
    }
}
